/*
 * Copyright 2007-2009 Perttu "celeron55" Ahola <celeron55@gmail.com>
 *
 * This file is part of jukuMagic.
 *
 * jukuMagic is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jukuMagic is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with jukuMagic.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "main.h"

IMPLEMENT_APP(JukuApp)

void DrawOpaqueRectangle(wxWindowDC &dc, wxPoint point, wxSize size)
{
	for(int y0=0; y0<size.GetHeight(); y0++){
		for(int x0=y0%2; x0<size.GetWidth(); x0+=2){
			dc.DrawPoint(point.x+x0, point.y+y0);
		}
	}
}

JukuApp::JukuApp()
{
	//verbose_mode_on = false;
	//fast_mode_on = false;
}

/*bool JukuApp::OnInit()
{
	int e = wxApp::OnInit();
	if(e != 0) return e;
    JukuFrame *frame = new JukuFrame( _T("jukuMagic"), wxPoint(50,50), wxSize(400,300) );
    frame->Show(TRUE);
    SetTopWindow(frame);
    return true;
}*/

int JukuApp::OnExit()
{
	cout<<"JukuApp::OnExit()"<<endl;
	return true;
}

void JukuApp::OnInitCmdLine(wxCmdLineParser& parser)
{
    parser.SetDesc (g_cmdLineDesc);
    // must refuse '/' as parameter starter or cannot use "/path" style paths
    parser.SetSwitchChars (wxT("-"));
}
 
bool JukuApp::OnCmdLineParsed(wxCmdLineParser& parser)
{
	JukuFrame *frame = new JukuFrame(_T("jukuMagic"), wxPoint(-1,-1), wxSize(780,600));
	//annetaan framelle komentorivin sisältö ja palautetaan sen palautusarvo
	bool cmd_line_accepted = frame->CmdLine(parser);

	if(cmd_line_accepted){
		frame->Show(TRUE);
		SetTopWindow(frame);
		return true;
	}
	return false;
}

/////////////////////////////

JukuFrame::JukuFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
: wxFrame((wxFrame *)NULL, -1, title, pos, size)
{
	verbose_mode_on = false;
	waiting_static_place_selection = false;

    wxMenu *menu0 = new wxMenu;

    menu0->Append( ID_Open, _T("&Open") );
    menu0->AppendSeparator();
    menu0->Append( ID_About, _T("&About") );
    menu0->AppendSeparator();
    menu0->Append( ID_Quit, _T("E&xit") );

    wxMenuBar *menuBar = new wxMenuBar;
    menuBar->Append( menu0, _T("&Menu") );

    SetMenuBar( menuBar );

	wxBoxSizer *topsizer = new wxBoxSizer( wxVERTICAL );
	
	board = new Board();

    boardwindow = new BoardWindow((wxWindow*)this, WindowID_BoardWindow);
	boardwindow->SetBoard(board);

	boardw_scroll = new wxScrollBar((wxWindow*)this, WindowID_BoardWidthScroll);
	boardw_scroll->SetScrollbar(1, 20, 20+70, 1);
	//boardw_scroll->SetScrollbar(1, 20, 120, 1);
	boardh_scroll = new wxScrollBar((wxWindow*)this, WindowID_BoardHeightScroll);
	boardh_scroll->SetScrollbar(1, 20, 20+70, 1);
	//boardh_scroll->SetScrollbar(1, 20, 120, 1);

	wxButton *genbutton = new wxButton((wxWindow*)this, WindowID_GenerateButton, wxT("Generate"));
	boardsize_statictext = new wxStaticText((wxWindow*)this, -1, wxT(""),
			wxDefaultPosition, wxSize(150, -1));
	
	autogenerate_check = new wxCheckBox((wxWindow*)this, -1, wxT("Auto generate"));

	wxButton *setstaticbutton = new wxButton((wxWindow*)this,
			WindowID_SetStaticButton, wxT("Set Static"));
	wxButton *setmovablebutton = new wxButton((wxWindow*)this,
			WindowID_SetMovableButton, wxT("Set Movable"));

	infolabel0 = new wxStaticText((wxWindow*)this, -1, wxT("<infolabel0>"),
			wxDefaultPosition, wxSize(-1, -1));
	
	partlist = new wxListBox((wxWindow*)this, -1, wxDefaultPosition,
			wxSize(-1, -1), 0, NULL, wxLB_SINGLE);
	
	wxBoxSizer *toprowsizer = new wxBoxSizer( wxHORIZONTAL );
	wxBoxSizer *middlehboxsizer = new wxBoxSizer( wxHORIZONTAL );
	wxBoxSizer *rightvboxsizer = new wxBoxSizer( wxVERTICAL );
	wxBoxSizer *righttophboxsizer = new wxBoxSizer( wxHORIZONTAL );
	
	toprowsizer->Add(autogenerate_check, 0, 0, 0);
	toprowsizer->Add(genbutton, 0, 0, 0);
	//toprowsizer->Add(genexbutton, 0, 0, 0);
	toprowsizer->Add(boardw_scroll, 1, wxEXPAND, 0);
	toprowsizer->Add(boardh_scroll, 1, wxEXPAND, 0);
	toprowsizer->Add(boardsize_statictext, 0, 0, 5);

	righttophboxsizer->Add(setstaticbutton, 0, 0, 0);
	righttophboxsizer->Add(setmovablebutton, 0, 0, 0);

	rightvboxsizer->Add(infolabel0, 0, 0, 0);
	rightvboxsizer->Add(righttophboxsizer, 0, 0, 0);
	rightvboxsizer->Add(partlist, 1, wxEXPAND, 0);

	middlehboxsizer->Add(boardwindow, 2, wxEXPAND, 0);
	middlehboxsizer->Add(rightvboxsizer, 1, wxEXPAND, 0);

	topsizer->Add(toprowsizer, 0, wxEXPAND, 5);
	topsizer->Add(middlehboxsizer, 1, wxEXPAND, 5);

	SetSizer(topsizer);

    CreateStatusBar();
    SetStatusText(wxT("jukuMagic by Perttu Ahola"));
}

bool JukuFrame::CmdLine(wxCmdLineParser &parser)
{
	verbose_mode_on = parser.Found(wxT("v"));
	debugmessages_on = verbose_mode_on;
	
	long int tempboardw, tempboardh;
	bool w_specified = parser.Found(wxT("W"), &tempboardw);
	if(!w_specified) boardw = 15; //TODO: default-arvot tässä?
	else boardw = tempboardw;
	boardw_scroll->SetThumbPosition(boardw);
	bool h_specified = parser.Found(wxT("H"), &tempboardh);
	if(!h_specified) boardh = 15; //TODO: ^
	else boardh = tempboardh;
	boardh_scroll->SetThumbPosition(boardh);
	
	UpdateBoardSizeText();
	
	if(!parser.Found(wxT("a"))) autogenerate_check->SetValue(true);

	if(parser.GetParamCount()>0){
		wxString file_to_open = parser.GetParam(0);
		if(OpenFile(file_to_open)){
			//cout<<"Opened file"<<endl;
		}
		else{
			cout<<"Opening file failed"<<endl;
			Close(TRUE);
		}
		return true;
	}
	return true;
}

bool JukuFrame::OpenFile(wxString filename)
{
	if(circuit.LoadFile((std::string)filename.mb_str()) == false){
		//cout<<"opening file failed"<<endl;
    	SetStatusText(wxT("Failed to open ") + filename);
		return false;
	}
    SetStatusText(wxT("Opened ") + filename);
	//while(!ReGenerateBoard() && wanted_maxwidth < 100) wanted_maxwidth++;
	if(autogenerate_check->GetValue()==true){
		ReGenerateBoard();
	}
	return true;
}

bool JukuFrame::ReGenerateBoard()
{
	//Board board;
	if(!board->Generate(&circuit, boardw, boardh)){
		cout<<"generating failed"<<endl;
    	SetStatusText(wxT("Generating failed (too small size?)"));
		RefreshBoardInfo();
		boardwindow->ReDrawBoard();
		return false;
	}
    SetStatusText(wxT("Generating succeeded"));
	/*if(board->Valid()){
	}*/
	RefreshBoardInfo();
	//boardwindow->SetBoard(board);
	boardwindow->ReDrawBoard();
	return true;
}

void JukuFrame::RefreshBoardInfo()
{
	partlistinfo.clear();

	vector<Wire> wires = board->GetWires();

	wxString sinfo;
	sinfo.Printf(wxT("Width: %d Height: %d Wires: %d"), board->Width(),
			board->Height(), wires.size());
	infolabel0->SetLabel(sinfo);

	//lisäillään osat listaan
	
	wxArrayString items;

	vector<Part> parts = circuit.Parts();
	for(unsigned int i=0; i<parts.size(); i++){
		Part p = parts[i];
		wxString s(p.Name().c_str(), wxConvUTF8);
		wxString sinfo;
		Coord c = p.GetCoord();
		if(c.exist){
			sinfo.Printf(wxT("P%d @(%d,%d): "), i, c.x, c.y);
		}
		else{
			sinfo.Printf(wxT("P%d: "), i);
		}
		sinfo += s + wxT(": ");
		vector<Foot> foots = p.GetFootprint()->Foots();
		for(unsigned int j=0; j<foots.size(); j++){
			wxString snumber = wxString::Format(wxT("%d"), foots[j].number);
			sinfo += wxT("[") + snumber;
			if(foots[j].name!=""){
				wxString sname(foots[j].name.c_str(), wxConvUTF8);
				sinfo += wxT(":") + sname;
			}
			sinfo += wxT("]");
		}

		items.Add(sinfo);

		PartListItemInfo info;
		info.type = IT_PART;
		info.num = i;
		partlistinfo.push_back(info);
	}
	
	vector<WirePart> wparts = circuit.WireParts();
	for(unsigned int i=0; i<wparts.size(); i++){
		WirePart wp = wparts[i];
		wxString sfn1 = wxString::Format(wxT("%d"), wp.FootNum1());
		wxString sfn2 = wxString::Format(wxT("%d"), wp.FootNum2());
		wxString s(wp.Name().c_str(), wxConvUTF8);
		wxString sinfo;
		sinfo.Printf(wxT("WP%d: "), i);
		sinfo += s + wxT(": [");
		sinfo += sfn1;
		if(wp.FootName1()!=""){
			wxString s(wp.FootName1().c_str(), wxConvUTF8);
			sinfo += wxT(":") + s;
		}
		sinfo += wxT("][");
		sinfo += sfn2;
		if(wp.FootName2()!=""){
			wxString s(wp.FootName2().c_str(), wxConvUTF8);
			sinfo += wxT(":") + s;
		}
		sinfo += wxT("]");

		items.Add(sinfo);

		PartListItemInfo info;
		info.type = IT_WPART;
		info.num = i;
		partlistinfo.push_back(info);
	}

	if(board->Valid()){
		vector<Wire> wires = board->GetWires();
		for(unsigned int i=0; i<wires.size(); i++){
			Wire w = wires[i];
			//w.c1.x jne
			wxString sinfo;
			sinfo.Printf(wxT("W%d: (%d,%d)-(%d,%d)"), i, w.c1.x, w.c1.y, w.c2.x, w.c2.y);

			items.Add(sinfo);

			PartListItemInfo info;
			info.type = IT_WIRE;
			info.num = i;
			partlistinfo.push_back(info);
		}
	}
	partlist->Set(items);
}

void JukuFrame::OnGenerateButton(wxCommandEvent& event)
{
	ReGenerateBoard();
	/*while(!ReGenerateBoard() && wanted_maxwidth < 100) wanted_maxwidth++;
	bw_scroll->SetThumbPosition(wanted_maxwidth);
	wxString s;
	s.Printf(wxT("Max Width: %d"), wanted_maxwidth);
	boardsize_statictext->SetLabel(s);*/
}

int JukuFrame::GetCircuitPartNumFromPartListSelection()
{
	wxArrayInt ai;
	int count = partlist->GetSelections(ai);
	if(count!=1){
		cout<<"invalid count of selections (1 supported and allowed by list)"<<endl;
		return -1;
	}
	int selectednumlist = ai.Item(0);
	cout<<"selectednumlist="<<selectednumlist<<endl;
	if(selectednumlist < 0 || selectednumlist >= (int)partlistinfo.size()){
		cout<<"invalid selectednumlist"<<endl;
		return -1;
	}
	if(partlistinfo[selectednumlist].type != IT_PART){
		cout<<"static moving of parts only supported"<<endl;
		return -1;
	}
	return partlistinfo[selectednumlist].num;
	//int bpartnum = partlistinfo[selectednumlist].num;

	/*vector<Part> bparts = board->GetParts(); //HUOM: tämä on aika raskas kopiointi

	vector<Part> cparts = circuit.Parts();
	for(unsigned int i=0; i<cparts.size(); i++){
		if(cparts[i].Name() == bparts[bpartnum].Name()){
			return i;
		}
	}*/
	return -1;
}

void JukuFrame::OnSetStaticButton(wxCommandEvent& event)
{
	cout<<"JukuFrame::OnSetStaticButton"<<endl;
	int cpartnum = GetCircuitPartNumFromPartListSelection();
	if(cpartnum==-1){
		cout<<"part not found from circuit"<<endl;
		return;
	}
	vector<Part> *cparts = circuit.PartsP();
	cout<<"Setting static place for part named \""<<(*cparts)[cpartnum].Name()<<"\"."<<endl;
	//(*cparts)[cpartnum].SetCoord(MakeCoord(4, 4));
	Footprint *fp = (*cparts)[cpartnum].GetFootprint();
	boardwindow->SetMouseHilightSize(fp->Width(), fp->Height());
	waiting_static_place_selection = true;
	cpartnum_waiting_static_place_selection = cpartnum;
}

void JukuFrame::OnSetMovableButton(wxCommandEvent& event)
{
	cout<<"JukuFrame::OnSetMovableButton"<<endl;
	int cpartnum = GetCircuitPartNumFromPartListSelection();
	if(cpartnum==-1){
		cout<<"part not found from circuit"<<endl;
		return;
	}
	vector<Part> *cparts = circuit.PartsP();
	cout<<"Setting non-static place for part named \""<<(*cparts)[cpartnum].Name()<<"\"."<<endl;
	(*cparts)[cpartnum].SetLocked(false);
	(*cparts)[cpartnum].SetCoord(MakeInexistentCoord());

	if(autogenerate_check->GetValue()==true){
		ReGenerateBoard();
	}
}

void JukuFrame::UpdateBoardSizeText()
{
	wxString s;
	s.Printf(wxT("Width: %d Height: %d"), boardw, boardh);
	boardsize_statictext->SetLabel(s);
}

void JukuFrame::OnBoardWidthScroll(wxScrollEvent& event)
{
	unsigned int newwidth = boardw_scroll->GetThumbPosition();
	if(newwidth!=boardw){
		boardw = newwidth;
		if(autogenerate_check->GetValue()==true){
			ReGenerateBoard();
		}
		UpdateBoardSizeText();
	}
}

void JukuFrame::OnBoardHeightScroll(wxScrollEvent& event)
{
	unsigned int newheight = boardh_scroll->GetThumbPosition();
	if(newheight!=boardh){
		boardh = newheight;
		if(autogenerate_check->GetValue()==true){
			ReGenerateBoard();
		}
		UpdateBoardSizeText();
	}
}

void JukuFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
	delete board;
    Close(TRUE);
}

void JukuFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
    wxMessageBox(_T("jukuMagic - A stripboard layout generator"
			" by Perttu Ahola <celeron55@gmail.com>"),
        _T("About jukuMagic"), wxOK | wxICON_INFORMATION, this);
}

void JukuFrame::OnOpen(wxCommandEvent& WXUNUSED(event))
{
	cout<<"JukuFrame::OnOpen()"<<endl;
	wxFileDialog fd((wxWindow*)this);
	if(fd.ShowModal() == wxID_OK){
		OpenFile(fd.GetPath());
	}
}

void JukuFrame::OnBWLeftClick(wxCommandEvent& event)
{
	cout<<"JukuFrame::OnBWLeftClick"<<endl;
	unsigned int cx, cy;
	unsigned int i = event.GetInt();
	cx = i & 0xffff;
	cy = i >> 16;
	//cout<<"click at ("<<cx<<","<<cy<<")"<<endl;
	if(waiting_static_place_selection){
		vector<Part> *cparts = circuit.PartsP();
		cout<<"Selected static place for part named \""
				<<(*cparts)[cpartnum_waiting_static_place_selection].Name()<<"\" at ("
				<<cx<<","<<cy<<")"<<endl;
		(*cparts)[cpartnum_waiting_static_place_selection].SetLocked(false);
		(*cparts)[cpartnum_waiting_static_place_selection].SetCoord(MakeCoord(cx, cy));
		(*cparts)[cpartnum_waiting_static_place_selection].SetLocked(true);

		boardwindow->SetMouseHilightSize(0, 0);
		waiting_static_place_selection = false;

		if(autogenerate_check->GetValue()==true){
			ReGenerateBoard();
		}
	}
}

void JukuFrame::OnBWRightClick(wxCommandEvent& event)
{
	cout<<"JukuFrame::OnBWRightClick"<<endl;
	if(waiting_static_place_selection){
		boardwindow->SetMouseHilightSize(0, 0);
		waiting_static_place_selection = false;
	}
}

/////////////////////////////////////

BoardWindow::BoardWindow(wxWindow *parent, wxWindowID id)
: wxScrolledWindow(parent, id)
{
	SetScrollRate(BWSCROLLRATE, BWSCROLLRATE);
	hilight_wanted = false;
	hilight_valid = false;
	hilightredrawtimer = new wxTimer((wxEvtHandler*)this, TimerID_BoardWindow_HilightRedraw);
	settings = new BoardWindowSettings(25, 20);
	//settings = new BoardWindowSettings(50, 20);
	//settings = new BoardWindowSettings(11, 20);
}

BoardWindow::~BoardWindow()
{
	delete hilightredrawtimer;
	delete settings;
}

void BoardWindow::SetBoard(Board *board_)
{
	board = board_;

	ReDrawBoard();
}

void BoardWindow::ReDrawBoard()
{
	Refresh(); //through event-loop
	//Update(); //without event loop
}

//#define FLIP_OLD
#define FLIP_NEW

void BoardWindow::OnPaint(wxPaintEvent& event)
{
	//wxScrolledWindow::OnPaint(event);

	wxPaintDC dc(this);
	DoPrepareDC(dc);

	//tehdään tässä, koska board voi, ja kuuluukin, olla muuttunut setboardin jälkeen
	settings->CalcValues(board);

	//GetVirtualSize(&settings->clientw, &settings->clienth);
	SetVirtualSize(settings->clientw, settings->clienth);

	wxColour backgroundcolor(255,255,255);
	wxColour coppercolor(184,115,51);
	wxColour solderbasecolor(140,140,140);
	wxColour solderlightcolor(190,190,190);
	wxColour solderdarkcolor(100,100,100);
	//wxColour boardbackground(208,197,70);
	wxColour boardbackground(208,195,70);
	wxColour boardbackground_bad(220,150,70);
	wxColour bordercolor(0,0,0);
	wxColour partlegcolor(0,0,0);
	//wxColour wirepartlegcolor(0,100,50);
	wxColour componentblockscolor(100,100,100);
	wxColour legnumcolor(255,255,255);
	//wxColour infotextcolor(0,0,0);
	wxColour mousehilightcolor(255,50,50);
	wxColour nfccolor(125,195,40);

	wxPen pen(backgroundcolor);
	wxBrush brush(backgroundcolor);
	dc.SetPen(pen);
	dc.SetBrush(brush);

	//tyhjätään ruutu
	//dc.Clear();
	//wxPoint zeropoint(0,0);
	wxSize dcsize = dc.GetSize();
	dc.DrawRectangle(0, 0, settings->clientw > dcsize.GetWidth() ? settings->clientw : dcsize.GetWidth(),
			settings->clienth > dcsize.GetHeight() ? settings->clienth : dcsize.GetHeight());
	//dc.DrawRectangle(zeropoint, dcsize);
	
	//===============päällypuoli===============
	
	//piirretään tausta
	pen.SetColour(bordercolor);
	dc.SetPen(pen);
	if(board->Valid()){
		brush.SetColour(boardbackground);
		dc.SetBrush(brush);
	}
	else{
		brush.SetColour(boardbackground_bad);
		dc.SetBrush(brush);
	}
	dc.DrawRectangle(settings->draw1startx, settings->draw1starty,
			board->Width()*settings->blocksize, board->Height()*settings->blocksize+settings->ybordersize*2);
	
	/*//väritetään nfc-alueet
	pen.SetColour(nfccolor);
	dc.SetPen(pen);
	brush.SetColour(nfccolor);
	dc.SetBrush(brush);
	for(unsigned int y=0; y<board->Height(); y++){
		for(unsigned int x=0; x<board->Width(); x++){
			if(board->Content(x, y).nfc_partnum != -1){
				unsigned int width = settings->blocksize;
				unsigned int height = settings->blocksize;
				wxPoint drawpoint(settings->draw1startx + x*settings->blocksize,
						settings->draw1starty + settings->ybordersize + y*settings->blocksize);
				wxSize  drawsize (width, height);
				dc.DrawRectangle(drawpoint, drawsize);
			}
		}
	}*/

	//piirretään reiät
	pen.SetColour(bordercolor);
	dc.SetPen(pen);
	brush.SetColour(backgroundcolor);
	dc.SetBrush(brush);
	for(unsigned int y=0; y<board->Height(); y++){
		for(unsigned int x=0; x<board->Width(); x++){
			dc.DrawCircle(settings->draw1startx + x*settings->blocksize + settings->blocksize/2,
					settings->draw1starty + settings->ybordersize + y*settings->blocksize + settings->blocksize/2,
					settings->hole_size);
		}
	}
	
	//tummennetaan blokattuja alueita
	pen.SetColour(componentblockscolor);
	dc.SetPen(pen);
	brush.SetColour(componentblockscolor);
	dc.SetBrush(brush);
	for(unsigned int y=0; y<board->Height(); y++){
		for(unsigned int x=0; x<board->Width(); x++){
			if(board->Content(x, y).partname_blocks != ""){
				bool do_decrease_left = (board->Content(x, y).partname_blocks
						!= board->Content(x-1, y).partname_blocks);
				bool do_decrease_right= (board->Content(x, y).partname_blocks
						!= board->Content(x+1, y).partname_blocks);
				bool do_decrease_top  = (board->Content(x, y).partname_blocks
						!= board->Content(x, y-1).partname_blocks);
				bool do_decrease_bottom=(board->Content(x, y).partname_blocks
						!= board->Content(x, y+1).partname_blocks);
				unsigned int leftd = do_decrease_left?settings->part_edge_decrease:0;
				unsigned int rightd = do_decrease_right?settings->part_edge_decrease:0;
				unsigned int topd = do_decrease_top?settings->part_edge_decrease:0;
				unsigned int bottomd = do_decrease_bottom?settings->part_edge_decrease:0;
				unsigned int width = settings->blocksize - leftd - rightd;
				unsigned int height = settings->blocksize - topd - bottomd;
				wxPoint drawpoint(settings->draw1startx + x*settings->blocksize + leftd,
						settings->draw1starty + settings->ybordersize + y*settings->blocksize + topd);
				wxSize  drawsize (width, height);
				DrawOpaqueRectangle(dc, drawpoint, drawsize);
			}
		}
	}

	//if(!hilight_valid){

	//merkataan kupareiden leikkaukset rasteilla
	pen.SetColour(bordercolor);
	dc.SetPen(pen);
	for(unsigned int y=0; y<board->Height(); y++){
		for(unsigned int x=0; x<board->Width(); x++){
			if(board->Content(x, y).type==CUT){
				dc.DrawLine(settings->draw1startx + x*settings->blocksize + settings->blocksize/4 + 2,
						settings->draw1starty + settings->ybordersize + y*settings->blocksize + settings->blocksize/4,
						settings->draw1startx + x*settings->blocksize + settings->blocksize - settings->blocksize/4 - 2,
						settings->draw1starty + settings->ybordersize + y*settings->blocksize + settings->blocksize - settings->blocksize/4);
				dc.DrawLine(settings->draw1startx + x*settings->blocksize + settings->blocksize - settings->blocksize/4 - 2,
						settings->draw1starty + settings->ybordersize + y*settings->blocksize + settings->blocksize/4,
						settings->draw1startx + x*settings->blocksize + settings->blocksize/4 + 2,
						settings->draw1starty + settings->ybordersize + y*settings->blocksize + settings->blocksize - settings->blocksize/4);
			}
		}
	}
	
	//piirretään hyppylangat
	pen.SetWidth(3);
	vector<Wire> wires = board->GetWires();
	for(unsigned int i=0; i<wires.size(); i++){
		Wire w = wires[i];

		//wxColour wirecolor((i*255)/wires.size(), 255 - (i*255)/wires.size(), 255);
		/*wxColour wirecolor((w.c1.y*(int)pow(w.c1.x+w.c2.y+10, 2))%256, (i*255)/wires.size(),
				255 - ((i*255)/wires.size()));*/
		/*wxColour wirecolor((i*255)/wires.size(), 255 - ((i*255)/wires.size()),
				(w.c1.y*(int)pow(w.c1.x+w.c2.y+10, 2))%256);*/
		unsigned char r = (i*255)/wires.size();
		unsigned char g = (i*48);
		unsigned char b = (i*192);
		//if(r>200 && g>200 && b>200){ r-=100; g-=100; b-=100 };
		wxColour wirecolor(r, g, b);
		pen.SetColour(wirecolor);
		dc.SetPen(pen);
		brush.SetColour(wirecolor);
		dc.SetBrush(brush);

		int h1x = settings->draw1startx + (w.c1.x)*settings->blocksize + settings->blocksize/2;
		int h1y = settings->draw1starty + settings->ybordersize + (w.c1.y)*settings->blocksize + settings->blocksize/2;
		int h2x = settings->draw1startx + (w.c2.x)*settings->blocksize + settings->blocksize/2;
		int h2y = settings->draw1starty + settings->ybordersize + (w.c2.y)*settings->blocksize + settings->blocksize/2;

		/*int h1x2 = h1x;
		int h1y2 = h1y - settings->wire_height;
		int h2x2 = h2x;
		int h2y2 = h2y - settings->wire_height;
		
		if(h1x==h2x){
			h1y2 = h1y;
			h2y2 = h2y;
			h1x2 += w.c1.x;
			h2x2 += w.c2.x;
		}
		else if(h1y==h2y){
			h1x2 += w.c1.y;
			h2x2 += w.c2.y;
		}*/

		int ychange = (h1y>h2y)?(h1y-h2y):(h2y-h1y);
		int xchange = (h1x>h2x)?(h1x-h2x):(h2x-h1x);
		
		int h1x2;
		int h1y2;
		int h2x2;
		int h2y2;
		
		if(xchange > ychange){
			h1y2 = h2y - settings->wire_height;
			h1x2 = h1x;
			h2y2 = h2y - settings->wire_height;
			h2x2 = h2x;
		}
		else{ //xhange <= ychange
			h1y2 = h1y;
			h1x2 = h1x - settings->wire_height;
			h2y2 = h2y;
			h2x2 = h1x - settings->wire_height;
		}

		/*h1y2 -= w.c1.x + w.c1.y;
		h2y2 -= w.c2.x + w.c2.y;*/

		//piirretään päihin ympyrät
		dc.DrawCircle(h1x, h1y, settings->blocksize/8);
		dc.DrawCircle(h2x, h2y, settings->blocksize/8);
		
		//piirretään viivat
		dc.DrawLine(h1x, h1y, h1x2, h1y2);
		dc.DrawLine(h2x, h2y, h2x2, h2y2);
		dc.DrawLine(h1x2, h1y2, h2x2, h2y2);
	}
	
	pen.SetWidth(1);
	
	//piirretään kiinteäjalkaiset osat
	
	vector<Part> parts = board->GetParts();
	for(unsigned int i=0; i<parts.size(); i++){
		Part p = parts[i];
		Coord pc = p.GetCoord();
		
		unsigned int footcounter = 0;
		vector< vector<Contentblock> > cblocks = p.GetFootprint()->Contentblocks();
		for(unsigned int y=0; y<cblocks.size(); y++){
			for(unsigned int x=0; x<cblocks[y].size(); x++){
				Contentblock b = cblocks[y][x];
				if(b.type==PFOOT){
					brush.SetColour(partlegcolor);
					dc.SetBrush(brush);
					pen.SetColour(partlegcolor);
					dc.SetPen(pen);
					dc.DrawCircle(settings->draw1startx + (pc.x+x)*settings->blocksize + settings->blocksize/2,
							settings->draw1starty + settings->ybordersize + (pc.y+y)*settings->blocksize + settings->blocksize/2,
							settings->blocksize/7);
					wxString snumber = wxString::Format(wxT("%d"), b.num);
					int tw, th;
					GetTextExtent(snumber, &tw, &th);
					dc.SetTextForeground(legnumcolor);
					dc.DrawText(snumber, settings->draw1startx + (pc.x+x)*settings->blocksize + settings->blocksize/2 - tw/2,
							settings->draw1starty + settings->ybordersize + (pc.y+y)*settings->blocksize);
					footcounter++;
				}
			}
		}
	}
	
	//piirretään johto-osat
	pen.SetWidth(1);
	vector<WirePart> wparts = board->GetWireParts();
	for(unsigned int i=0; i<wparts.size(); i++){
		WirePart wp = wparts[i];
		Coord pc1 = wp.C1();
		Coord pc2 = wp.C2();
		
		wxColour wirepartlegcolor((i*100)/wparts.size(), 100, 100 - (i*100)/wparts.size());
		brush.SetColour(wirepartlegcolor);
		dc.SetBrush(brush);
		pen.SetColour(wirepartlegcolor);
		dc.SetPen(pen);

		int h1x = settings->draw1startx + (pc1.x)*settings->blocksize + settings->blocksize/2;
		int h1y = settings->draw1starty + settings->ybordersize + (pc1.y)*settings->blocksize + settings->blocksize/2;
		int h2x = settings->draw1startx + (pc2.x)*settings->blocksize + settings->blocksize/2;
		int h2y = settings->draw1starty + settings->ybordersize + (pc2.y)*settings->blocksize + settings->blocksize/2;
		int h1x2 = h1x;
		int h1y2 = h1y - settings->wirepart_height;
		int h2x2 = h2x;
		int h2y2 = h2y - settings->wirepart_height;
		
		//piirretään päihin ympyrät
		dc.DrawCircle(h1x, h1y, settings->blocksize/7);
		dc.DrawCircle(h2x, h2y, settings->blocksize/7);
		
		//piirretään viivat
		dc.DrawLine(h1x, h1y, h1x2, h1y2);
		dc.DrawLine(h2x, h2y, h2x2, h2y2);
		dc.DrawLine(h1x2, h1y2, h2x2, h2y2);

		//kirjoitellaan päiden jalkanumerot
		wxString sfn1 = wxString::Format(wxT("%d"), wp.FootNum1());
		wxString sfn2 = wxString::Format(wxT("%d"), wp.FootNum2());
		dc.SetTextForeground(legnumcolor);
		dc.DrawText(sfn1, settings->draw1startx + (pc1.x)*settings->blocksize,
				settings->draw1starty + settings->ybordersize + (pc1.y)*settings->blocksize);
		dc.DrawText(sfn2, settings->draw1startx + (pc2.x)*settings->blocksize,
				settings->draw1starty + settings->ybordersize + (pc2.y)*settings->blocksize);
	}

	pen.SetWidth(1);

	//}

	//piirretään mahdollinen hilight
	pen.SetColour(mousehilightcolor);
	dc.SetPen(pen);
	brush.SetColour(mousehilightcolor);
	dc.SetBrush(brush);
	/*cout<<"mousehilightw = "<<mousehilightw<<" mousehilighth = "<<mousehilighth
			<<" mousehilightx = "<<mousehilightx<<" mousehilighty = "<<mousehilighty<<endl;*/
	//if(mousehilightw > 0 && mousehilighth > 0 && mousehilightx != -1 && mousehilighty != -1){
	if(hilight_valid){
		//cout<<"Drawing hilight to ("<<mousehilightx<<","<<mousehilighty<<")"<<endl;
		unsigned int x = mousehilightx;
		unsigned int y = mousehilighty;
		wxPoint drawpoint(settings->draw1startx + x*settings->blocksize,
				settings->draw1starty + settings->ybordersize + y*settings->blocksize);
		wxSize  drawsize (mousehilightw*settings->blocksize, mousehilighth*settings->blocksize);
		DrawOpaqueRectangle(dc, drawpoint, drawsize);
	}

	//===============pohjapuoli==============
	
	if(!hilight_valid){
	
	//piirretään tausta
	pen.SetColour(bordercolor);
	dc.SetPen(pen);
	brush.SetColour(boardbackground);
	dc.SetBrush(brush);
	dc.DrawRectangle(settings->draw2startx, settings->draw2starty,
			board->Width()*settings->blocksize, board->Height()*settings->blocksize+settings->ybordersize*2);
	//piirretään kupariraidat
	brush.SetColour(coppercolor);
	dc.SetBrush(brush);
	for(unsigned int y=0; y<board->Height(); y++){
		wxPoint drawpoint(settings->draw2startx, settings->draw2starty + settings->ybordersize + y*settings->blocksize + settings->blockspacing/2);
		wxSize  drawsize (board->Width()*settings->blocksize, settings->blocksize - settings->blockspacing);
		dc.DrawRectangle(drawpoint, drawsize);
	}
	//leikataan kuparit
	pen.SetColour(boardbackground);
	dc.SetPen(pen);
	brush.SetColour(boardbackground);
	dc.SetBrush(brush);
	for(unsigned int y=0; y<board->Height(); y++){
		for(unsigned int x=0; x<board->Width(); x++){
#ifdef FLIP_OLD
			if(board->Content(board->Width()-x-1, y).type==CUT){
#endif
#ifdef FLIP_NEW
			if(board->Content(x, board->Height()-y-1).type==CUT){
#endif
				wxPoint drawpoint(settings->draw2startx + x*settings->blocksize + (settings->blocksize - settings->cutwidth)/2,
						settings->draw2starty + settings->ybordersize + y*settings->blocksize);
				wxSize  drawsize (settings->cutwidth, settings->blocksize);
				dc.DrawRectangle(drawpoint, drawsize);
			}
		}
	}
	//piirretään reijät
	pen.SetColour(bordercolor);
	dc.SetPen(pen);
	brush.SetColour(backgroundcolor);
	dc.SetBrush(brush);
	for(unsigned int y=0; y<board->Height(); y++){
		for(unsigned int x=0; x<board->Width(); x++){
			wxPoint drawpoint(settings->draw2startx + x*settings->blocksize + settings->blocksize/2, 
					settings->draw2starty + settings->ybordersize + y*settings->blocksize + settings->blocksize/2);
			dc.DrawCircle(drawpoint, settings->hole_size);
		}
	}

	//piirretään juotokset
	for(unsigned int y=0; y<board->Height(); y++){
		for(unsigned int x=0; x<board->Width(); x++){
#ifdef FLIP_OLD
			if(board->Content(board->Width()-x-1, y).type==PFOOT
					|| board->Content(board->Width()-x-1, y).type==WPFOOT
					|| board->Content(board->Width()-x-1, y).type==WIREHEAD){
#endif
#ifdef FLIP_NEW
			if(board->Content(x, board->Height()-y-1).type==PFOOT
					|| board->Content(x, board->Height()-y-1).type==WPFOOT
					|| board->Content(x, board->Height()-y-1).type==WIREHEAD){
#endif
				//perusklöntti
				pen.SetWidth(2);
				pen.SetColour(solderlightcolor);
				dc.SetPen(pen);
				brush.SetColour(solderbasecolor);
				dc.SetBrush(brush);
				wxPoint drawpoint;
				drawpoint.x = settings->draw2startx + x*settings->blocksize + settings->blocksize/2;
				drawpoint.y = settings->draw2starty + settings->ybordersize + y*settings->blocksize + settings->blocksize/2;
				dc.DrawCircle(drawpoint, settings->solder_size);
				
				pen.SetWidth(1);
				//kirkas piste
				pen.SetColour(solderlightcolor);
				dc.SetPen(pen);
				brush.SetColour(solderlightcolor);
				dc.SetBrush(brush);
				drawpoint.x = settings->draw2startx+x*settings->blocksize+settings->blocksize/2 - settings->solderdotsize;
				drawpoint.y = settings->draw2starty+settings->ybordersize+y*settings->blocksize+settings->blocksize/2 - settings->solderdotsize;
				dc.DrawCircle(drawpoint, settings->solderdotsize);
				//tumma piste
				pen.SetColour(solderdarkcolor);
				dc.SetPen(pen);
				brush.SetColour(solderdarkcolor);
				dc.SetBrush(brush);
				drawpoint.x = settings->draw2startx+x*settings->blocksize+settings->blocksize/2 + settings->solderdotsize;
				drawpoint.y = settings->draw2starty+settings->ybordersize+y*settings->blocksize+settings->blocksize/2 + settings->solderdotsize;
				dc.DrawCircle(drawpoint, settings->solderdotsize);
			}
		}
	}
	}

}

Coord BoardWindow::GetBoardCoordFromMousePos(int mx, int my)
{
	int startx, starty;
	GetViewStart(&startx, &starty);
	int realx = mx + (startx*BWSCROLLRATE);
	int realy = my + (starty*BWSCROLLRATE);
	int boardx = (realx - settings->draw1startx) / settings->blocksize;
	int boardy = (realy - settings->draw1starty) / settings->blocksize;
	if(boardx < 0 || boardy < 0 || boardx >= (int)board->Width() || boardy >= (int)board->Height())
			return MakeInexistentCoord();
	return MakeCoord(boardx, boardy);
}

void BoardWindow::OnLeftUp(wxMouseEvent& event)
{
	event.Skip(); //recommended
	//cout<<"BoardWindow::OnLeftUp at ("<<event.GetX()<<","<<event.GetY()<<")"<<endl;
	/*Coord c = GetBoardCoordFromMousePos(event.GetX(), event.GetY());
	if(!c.exist){
		cout<<"Not on board"<<endl;
		return;
	}*/
	if(hilight_valid){
		//cout<<"Click at valid place, sending event"<<endl;
		wxCommandEvent ce(wxEVT_BWLEFTCLICK, GetId());
		unsigned int i = mousehilightx & 0xffff;
		i |= (mousehilighty << 16);
		ce.SetInt(i);
		GetEventHandler()->ProcessEvent(ce);
	}
	else{
		cout<<"Click at invalid place"<<endl;
	}
}

void BoardWindow::OnRightUp(wxMouseEvent& event)
{
	event.Skip(); //recommended
	//cout<<"BoardWindow::OnRightUp at ("<<event.GetX()<<","<<event.GetY()<<")"<<endl;
	wxCommandEvent ce(wxEVT_BWRIGHTCLICK, GetId());
	GetEventHandler()->ProcessEvent(ce);
}

void BoardWindow::OnMotion(wxMouseEvent& event)
{
	//cout<<"BoardWindow::OnMotion ("<<event.GetX()<<","<<event.GetY()<<")"<<endl;
	if(hilight_wanted){
		Coord c = GetBoardCoordFromMousePos(event.GetX(), event.GetY());
		if(!c.exist){
			//cout<<"Not on board"<<endl;
			hilight_valid = false;
			if(hilightredrawtimer->IsRunning()){
				hilightredrawtimer->Stop();
			}
			return;
		}
		//cout<<"Coordinates on board: ("<<c.x<<","<<c.y<<")"<<endl;
		if(hilight_wanted){
			mousehilightx = c.x;
			mousehilighty = c.y;
			hilight_valid = true;
			if(!hilightredrawtimer->IsRunning()){
				hilightredrawtimer->Start(250);
			}
		}
	}
}

void BoardWindow::OnHilightRedrawTimer(wxTimerEvent& event)
{
	//cout<<"OnHilightRedrawTimer"<<endl;
	ReDrawBoard();
}

void BoardWindow::SetMouseHilightSize(int w, int h)
{
	mousehilightw = w;
	mousehilighth = h;
	if(w>0 && h>0){
		hilight_wanted = true;
		//hilightredrawtimer->Start(250);
	}
	else{
		hilight_wanted = false;
		hilight_valid = false;
		if(hilightredrawtimer->IsRunning()){
			hilightredrawtimer->Stop();
		}
		ReDrawBoard();
	}
}

/////////////////////////////

BoardWindowSettings::BoardWindowSettings(unsigned int blocksize_, unsigned int spacing_)
{
	blocksize = blocksize_;
	spacing = spacing_;
}

void BoardWindowSettings::CalcValues(Board *board)
{
	ybordersize = blocksize/10;
	blockspacing = blocksize/10;
	wire_height = blocksize/2;
	wirepart_height = 0;//blocksize/4;
	part_edge_decrease = blocksize/6;
	hole_size = blocksize/5;
	cutwidth = blocksize;/*blocksize/2 + blocksize/4*/;
	solder_size = blocksize/3 + blocksize/9;
	solderdotsize = solder_size/4;
	
	draw1startx = spacing;
	draw1starty = spacing;

	draw2startx = draw1startx;
	draw2starty = draw1starty + board->Height() * blocksize + spacing;

	draw3startx = draw1startx + board->Width() * blocksize + spacing;
	draw3starty = draw1starty;
	
	clientw = draw3startx /*+ maxinfow + 150*/;
	clienth = draw2starty + board->Height() * blocksize + spacing*2;
}

